package com.pingan.mobilegw.mobileservice.invoke;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.pingan.mobilegw.adapterservice.MobileContext;
import com.pingan.mobilegw.adapterservice.MobileServiceInfo;
import com.pingan.mobilegw.adapterservice.ResultEnum;
import com.pingan.mobilegw.adapterservice.TargetResponseObject;
import com.pingan.mobilegw.adapterservice.annotation.OperationType;
import com.pingan.mobilegw.adapterservice.model.MapOperation;
import com.pingan.mobilegw.adapterservice.model.ToString;
import com.pingan.mobilegw.mobileservice.invoke.exception.MethodNotFoundException;
import com.pingan.mobilegw.mobileservice.invoke.exception.ParamMissingException;
import com.pingan.mobilegw.mobileservice.invoke.exception.ServiceNotFoundException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.OrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 服务调用器
 * 
 * @author shuai.ls
 * @version $Id: ServiceInvoker.java, v 0.1 2013-4-12 下午3:09:28 jiadao Exp $
 */
public class ServiceInvoker {

    /** 默认logger */
    private static final Log logger    = LogFactory.getLog(ServiceInvoker.class);


    /** 发布的服务map */
    private final Map<String, Map<String, MethodInfo>> services       = new HashMap<String, Map<String, MethodInfo>>();

    /** 服务拦截器 */
  //TODO  private final List<MobileServiceInterceptor>       interecptors   = new ArrayList<MobileServiceInterceptor>();

    /**
     * 注册拦截器
     * 
     * @param interecptor
     */
 /**TODO   public synchronized void registerInterecptor(MobileServiceInterceptor interecptor) {
        if (logger.isInfoEnabled()) {
            logger.info("注册拦截器：" + interecptor.getClass().getCanonicalName());
        }
        this.interecptors.add(interecptor);

        // 重新排序
        OrderComparator.sort(interecptors);
    }
*/

    /**
     * 服务注册
     * 
     * @param target 目标服务
     * @param ifClazz Class信息
     */
    public synchronized void registerService(Object target, Class<?> ifClazz) {

        if (logger.isInfoEnabled()) {
            logger.info("开始注册target:" + target + ",interfaceClazz:" + ifClazz);
        }

        Map<String, MethodInfo> methodname2mi = new HashMap<String, MethodInfo>();

        for (Method method : ifClazz.getMethods()) {

            if (null == AnnotationUtils.getAnnotation(method, OperationType.class)) {
                continue;
            }

            //解析出各个参数的信息
            Type[] types = method.getGenericParameterTypes();// 泛型类型

            MethodInfo mi = new MethodInfo();
            mi.setMethod(method);
            mi.setParamCount(types.length);
            mi.setTarget(target);
            mi.setParamType(types);

            if (logger.isInfoEnabled()) {
                logger.info("开始解析方法:" + method.getName());
                logger.info("方法信息：" + mi);
            }

            MethodInfo oldmi = methodname2mi.put(method.getName(), mi);
            if (null != oldmi) {
                throw new IllegalArgumentException("MobileService不支持方法重载：" + ifClazz.getName()
                                                   + "." + method.getName());
            }

        }
        this.services.put(ifClazz.getName(), methodname2mi);
        if (logger.isInfoEnabled()) {
            logger.info("注册成功target:" + target + ",interfaceClazz:" + ifClazz);
        }
    }

    /**
     * 获取该系统发布的服务列表
     * 
     * @return 服务列表
     */
    public List<MobileServiceInfo> listServices() {
        List<MobileServiceInfo> result = new ArrayList<MobileServiceInfo>();
        for (Entry<String, Map<String, MethodInfo>> entry : this.services.entrySet()) {
            String interfaceType = entry.getKey();

            for (Entry<String, MethodInfo> entry2 : entry.getValue().entrySet()) {
                String methodName = entry2.getKey();
                MethodInfo mi = entry2.getValue();

                MobileServiceInfo msi = new MobileServiceInfo();

                msi.setInterfaceType(interfaceType);

                msi.setMethodName(methodName);

                Annotation[] ans = mi.getMethod().getAnnotations();
                Map<String, String> anMap = new HashMap<String, String>();
                for (Annotation an : ans) {
                    if (an instanceof OperationType) {
                        anMap.put("OperationType", ((OperationType) an).value());
                        anMap.put("OperationTypeDesc", ((OperationType) an).desc());
                    }
                }
                msi.setAnnotations(anMap);

                Class<?>[] clazzs = mi.getMethod().getParameterTypes();
                List<String> paramClazzs = new ArrayList<String>(clazzs.length);
                for (Class<?> clazz : clazzs) {
                    paramClazzs.add(clazz.getCanonicalName());
                }
                msi.setParamType(paramClazzs);

                if (logger.isInfoEnabled()) {
                    logger.info("请求服务列表信息：" + msi);
                }

                result.add(msi);
            }
        }

        return result;
    }

    /**
     * 服务调用核心类
     * 
     * @param uniqueId 调用唯一ID
     * @param context 调用上下文
     * @param requestData 请求参数
     * @return 调用结果
     * @throws ServiceNotFoundException
     * @throws MethodNotFoundException
     * @throws java.io.IOException
     * @throws ParamMissingException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws java.lang.reflect.InvocationTargetException
     */
    public TargetResponseObject invokeService(String uniqueId, MobileContext context,
                                              String requestData) throws ServiceNotFoundException,
                                                                 MethodNotFoundException,
                                                                 IOException,
                                                                 ParamMissingException,
                                                                 IllegalArgumentException,
                                                                 IllegalAccessException,
                                                                 InvocationTargetException {

        // 获取该接口的全部方法
        Map<String, MethodInfo> methodname2mi = this.services.get(context.getClassName());
        if (null == methodname2mi) {
            throw new ServiceNotFoundException(context.getSysName(), context.getClassName());
        }

        // 获取方法信息
        MethodInfo mi = methodname2mi.get(context.getMethodName());
        if (null == mi) {
            throw new MethodNotFoundException(context.getClassName(), context.getMethodName());
        }

        //检查参数数量
        List<Object> params = JSON.parseArray(requestData, mi.getParamType());
        if (params.size() != mi.getParamCount()) {
            throw new ParamMissingException(context.getMethodName(), mi.getParamCount());
        }

        //设置headers
        MobileRpcHolder.setHeaders(context.getHeaders());

        if (null != context.getSession()) {
            MobileRpcHolder.setSession(context.getSession());
        } else {
        }

        //调用方法
        String resultJson = invoke0(mi, params, uniqueId);

        //组装响应结果
        TargetResponseObject response = new TargetResponseObject();
        response.setUniqueId(uniqueId);
        response.setResultStatus(ResultEnum.ResultSuccess.getCode());
        response.setMemo(ResultEnum.ResultSuccess.getMemo());//memo应该不用传，在gw用code就可以反查出来
        response.setResult(resultJson);
        response.setChangeAttributes((Map<String, MapOperation>) MobileRpcHolder
            .getIncrementalChanges());

        return response;
    }

    /**
     * 调用
     *
     * @param mi
     * @param params
     * @return
     * @throws IllegalAccessException
     * @throws java.lang.reflect.InvocationTargetException
     */
    private String invoke0(MethodInfo mi, List<Object> params, String uniqueId)
                                                                               throws IllegalAccessException,
                                                                               InvocationTargetException {
        // 调用参数准备
        Method method = mi.getMethod();
        ReflectionUtils.makeAccessible(method);
        Object[] args = params.toArray();
        Object target = mi.getTarget();
        Object resultValue = null;
/**TODO
        // 调用前拦截
        for (MobileServiceInterceptor interecptor : interecptors) {
            Object ret = interecptor.beforeInvoke(method, args, target);
            if (ret != null) {
                resultValue = ret;
                break;
            }
        }
*/
        // 执行调用
        if (resultValue == null) {
            try {
                resultValue = method.invoke(mi.getTarget(), args);
            } catch (InvocationTargetException e) {

                // 异常拦截
                Throwable t = e.getCause();
               /**TODO for (MobileServiceInterceptor interecptor : interecptors) {
                    interecptor.throwsInvoke(t, method, args, target);
                }*/
                throw e;
            }
        }

        // 调用后拦截，入参为结果对象
        /**TODO
        for (MobileServiceInterceptor interecptor : interecptors) {
            interecptor.afterInvoke(resultValue, method, args, target);
        }
        */
        // 打印响应结果
        if (logger.isInfoEnabled()) {
            logger.info("【" + uniqueId + "】业务响应结果：" + ToString.toString(resultValue));
        }

        String resultJsonString = JSON.toJSONString(resultValue,
            SerializerFeature.DisableCircularReferenceDetect);

        // 调用后拦截,入参为jsonString
       /**TODO
        for (MobileServiceInterceptor interecptor : interecptors) {
            String resultJson = interecptor.afterInvoke(resultJsonString, method, args, target);
            if (StringUtils.isNotBlank(resultJson)) {
                return resultJson;
            }
        }
        */
        return resultJsonString;
    }
}
